<?php

/**
 * An authorization adapter for AuthComponent.  Provides the ability to authorize using the AclComponent,
 * If AclComponent is not already loaded it will be loaded using the Controller's ComponentCollection.
 *
 * PHP versions 5
 * CAKEPHP versions 2.x
 * 
 * Green CMS - Content Management System and Framework Powerfull by Cakephp
 * Copyright 2012, GREEN GLOBAL CO., LTD (toancauxanh.vn)
 * 
 * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
 * Copyright 2005-2011, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
 *
 * Licensed under The MIT License
 * Redistributions of files must retain the above copyright notice.
 *
 * @author        Technology Lab No.I <tech1@toancauxanh.vn>
 * @link          
 * @package       Controller.Component.Auth
 * @since         Green v 1.0
 * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
 */
App::uses('BaseAuthorize', 'Controller/Component/Auth');

class ActionsAuthorize extends BaseAuthorize {

    /**
     * Settings for authorize objects.
     *
     * - `actionPath` - The path to ACO nodes that contains the nodes for controllers.  Used as a prefix
     *    when calling $this->action();
     * - `actionMap` - Action -> crud mappings. Used by authorization objects that want to map actions to CRUD roles.
     * - `userModel` - Model name that ARO records can be found under.  Defaults to 'User'.
     * - `extractKey` - path to extract key from user data, default get userModel primary key data
     * - `adminKeys` - Value of user group admin full control access.
     * - `publicKeys` - Value of user group public.
     * @var array
     */
    public $settings = array(
        'actionPath' => 'controllers',
        'actionMap' => null,
        'userModel' => 'User.User',
    );

    /**
     * Checks user authorization.
     *
     * @param array $user Active user data, an empty data to check public key
     * @param CakeRequest $request
     * @return boolean false on unauthorize,true on authorize, an array allowed actions or '*' to allowed all action
     */
    public function authorize($user, CakeRequest $request) {
        $conditions = array();
        $setting = $this->settings;
        $Model = ClassRegistry::init($setting['userModel']);

        if (!empty($user[$Model->primaryKey])) {
            $conditions[] = array(
                'Aro.model' => $Model->alias,
                'Aro.foreign_key' => $user[$Model->primaryKey]
            );
        }

        if (($keys = $Model->getRoles($user,true))) {
            if ($Model->isAdministrator($keys)) {
                return '*';
            }
            $conditions[] = array(
                'Aro.model' => $Model->Role->alias,
                'Aro.foreign_key' => $keys
            );
        }

        if (empty($conditions)) {
            return false;
        }

        $Acl = $this->_Collection->load('Acl');
        $aro = $Acl->Aro->find('list', array(
            'callbacks' => false,
            'conditions' => array('or' => $conditions),
            'fields' => array('Aro.id'),
            'recursive' => -1));

        if (empty($aro)) {
            return false;
        }

        $mapActions = array();
        $node = $Acl->Aco->node($this->action($request));
        if (!empty($node)) {
            $mapActions = $Acl->Aco->find('list', array(
                'callbacks' => false,
                'conditions' => array(
                    'Aco.parent_id' => $node[0]['Aco']['id']
                ),
                'fields' => array('Aco.id', 'Aco.alias'),
                'recursive' => -1));
        }

        if (empty($mapActions)) {
            return false;
        }

        $allowedActions = $Acl->Aco->Permission->find('list', array(
            'callbacks' => false,
            'conditions' => array(
                'Permission.aro_id' => $aro,
                'Permission.aco_id' => array_keys($mapActions),
                'Permission._create' => 1,
                'Permission._read' => 1,
                'Permission._update' => 1,
                'Permission._delete' => 1,
            ),
            'fields' => array('Permission.aco_id', 'Permission.id'),
            'recursive' => -1));

        if (empty($allowedActions)) {
            return false;
        }

        return array_intersect_key($mapActions, $allowedActions);
    }

    /**
     * Get the action path for a given request.  Primarily used by authorize objects
     * that need to get information about the plugin, controller, and action being invoked.
     *
     * @param CakeRequest $request The request a path is needed for.
     * @param string $path
     * @return string the action path for the given request.
     */
    public function action($request, $path = '/:plugin/:controller') {
        $plugin = empty($request['plugin']) ? null : Inflector::camelize($request['plugin']) . '/';
        $path = str_replace(
                array(':controller', ':plugin/'), array(Inflector::camelize($request['controller']), $plugin), $this->settings['actionPath'] . $path
        );
        $path = str_replace('//', '/', $path);
        return trim($path, '/');
    }

}
